#pragma once

#include <iostream>
#include <pthread.h>
#include <vector>
#include <cassert>
#include <functional>
#include <semaphore.h> //信号量所需的头文件

using std::cerr;
using std::cout;
using std::endl;

static const int gcap = 5; // 刚开始给的环形队列的最大空间

template <class T>
class RingQueue
{
public:
    RingQueue(const int &cap = gcap)
        : _queue(cap), // 环形队列初始化开辟cap个空间
          _maxcap(cap)
    {
        int n = sem_init(&_spacesem, 0, _maxcap); // 初始化两个信号量
        assert(0 == n);
        n = sem_init(&_datasem, 0, 0);
        assert(0 == n);

        _ProducerStep = _ConsumerStep = 0; // 刚开始进入队列，生产者和消费者指向同一个位置

        pthread_mutex_init(&_pmutex, nullptr);
        pthread_mutex_init(&_cmutex, nullptr);
    }
    void Push(const T &in) // 不需要加锁
    {
        P(_spacesem); // 申请到了空间信号量 —— 能够进行正常的生产
        pthread_mutex_lock(&_pmutex);////////////////////////////////////////加锁，保证生产与生产是互斥，消费与消费是互斥，生产与消费是同步切互斥
        _queue[_ProducerStep++] = in;
        _ProducerStep %= _maxcap; // 数组模拟环形队列
        pthread_mutex_unlock(&_pmutex);
        V(_datasem);              // 生产者没有归还空间，因为数据占着空间了，但是我们多了数据，所以V数据信号量
    }
    void Pop(T *out)
    {
        P(_datasem); // 申请数据信号量 —— 能够进行正常的消费
        pthread_mutex_lock(&_cmutex);
        *out = _queue[_ConsumerStep++];
        _ConsumerStep %= _maxcap;
        pthread_mutex_unlock(&_cmutex);
        V(_spacesem); // 数据被消费了之后还了吗？没有，但是我们多了消费之后的空间，所以V空间信号量
    }
    ~RingQueue()
    {
        sem_destroy(&_spacesem);
        sem_destroy(&_datasem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }

private:
    void P(sem_t &sem) // pv操作
    {
        int n = sem_wait(&sem);
        // assert(0 == n);
        // (void)n;
        if (n != 0)
            printf("P操作失败!\n");
    }
    void V(sem_t &sem)
    {
        int n = sem_post(&sem);
        // assert(0 == n);
        // (void)n;
        if (n != 0)
            printf("V操作失败!\n");
    }

private:
    std::vector<T> _queue;   // 环形队列
    int _maxcap;             // 环形队列的最大容量
    sem_t _spacesem;         // 生产者看中的资源 —— 空间资源
    sem_t _datasem;          // 消费者看中的资源 —— 数据资源
    int _ProducerStep;       // 生产者在队列的位置
    int _ConsumerStep;       // 消费者在队列的位置
    pthread_mutex_t _pmutex; // 生产者的锁
    pthread_mutex_t _cmutex; // 消费者的锁
};